--
-- Arms/prot warrior rotation helper
--

-- '_G' stuff is to keep lualint happy
local _G = _G
if _G['select'](2, _G['UnitClass']('player')) ~= 'WARRIOR' then return end

-------------------------------------------
-- Local references to globals for speed and lint's happiness
-------------------------------------------
local UnitHealth = _G['UnitHealth']
local strsplit = _G['strsplit']
local max = _G['max']
local GetTime = _G['GetTime']

local Rotor = _G['Rotor']

--------------------
local MODULE = 'Warrior'

-------------------------------------------
-- Constants
-------------------------------------------

--
-- Spell ids
--
local MS_ID             = 12294
local REND_ID           = 772
local CSMASH_ID         = 86346
local OVERPOWER_ID      = 7384
local SLAM_ID           = 1464
local EXEC_ID           = 5308
local HS_ID             = 78
local VRUSH_ID          = 34428
local BERS_RAGE_ID      = 18499
local EXEC_BUFF_ID      = 90806
local RECKL_ID          = 1719
local DCALM_ID          = 85730
local HLEAP_ID          = 6544
local PUMMEL_ID         = 6552
local BLADESTORM_ID     = 46924
local CHARGE_ID         = 100
local BSHOUT_ID         = 6673
local CSHOUT_ID         = 469
local SSTRIKES_ID       = 12328
local TCLAP_ID          = 6343
local DSHOUT_ID         = 1160
local SHOCKWAVE_ID      = 46968
local CONCBLOW_ID       = 12809
local REVENGE_ID        = 6572
local DEVASTATE_ID      = 20243
local SHIELD_SLAM_ID    = 23922
local SHIELD_WALL_ID    = 871
local LAST_STAND_ID     = 12975

-- The module
local Mod = Rotor:NewModule(MODULE)

local IsArms = false
local IsProt = false

---------------------------------------------------------
--
-- Spell prediction
--

-- Spell objects
local ms            -- mortal strike
local csmash        -- colossus smash
local overpower
local exec          -- execution
local hs            -- heroic strike
local vrush         -- victory rush
local bers_rage     -- berserker rage
local bshout        -- battle shout
local charge
local shield_slam
local revenge
local devastate
local shockwave
local concblow
local rend
local tclap
local hleap
local dshout
local cshout

-- Buff objects
local exec_buff

-- Debuff objects
local rend_debuff
local csmash_debuff
local dshout_debuff
local tclap_debuff

-- Local vars
local now = 0
local gcd = 0
local first_csmash = true

--
-- Methods implementation
--

function Mod:RegisterAuras()
    Rotor:initBlIndicator(4)
    if IsArms then
        --
        -- Arms debuffs
        --
        csmash_debuff = Rotor:RegisterDebuff(CSMASH_ID, true)
        exec_buff = Rotor:RegisterSelfBuff(EXEC_BUFF_ID)
        Rotor:RegisterSelfBuff(RECKL_ID, 1)
        rend_debuff = Rotor:RegisterDebuff(REND_ID, true)
    elseif IsProt then
        --
        -- Prot debuffs
        --
        Rotor:RegisterSelfBuff(SHIELD_WALL_ID, 1)
        Rotor:RegisterSelfBuff(LAST_STAND_ID, 1)
        dshout_debuff = Rotor:RegisterDebuff(DSHOUT_ID, false)
        tclap_debuff = Rotor:RegisterDebuff(TCLAP_ID, false)
        rend_debuff = Rotor:RegisterDebuff(REND_ID, true, 3, 'reminder')
    end
end

function Mod:RegisterSpells()
    local spell

    bshout = Rotor:RegisterSpell(BSHOUT_ID)
    cshout = Rotor:RegisterSpell(CSHOUT_ID)
    hleap = Rotor:RegisterSpell(HLEAP_ID, 10)
    Rotor:RegisterOffGcdSpell(PUMMEL_ID, 11)
    charge = Rotor:RegisterOffGcdSpell(CHARGE_ID, nil, 9, 'reminder')
    charge.range_check = true
    spell = Rotor:RegisterOffGcdSpell(HS_ID, nil, 12, 'reminder')
    spell.min_power = 65
    spell.range_check = true

    if IsArms then
        ms = Rotor:RegisterSpell(MS_ID)
        csmash = Rotor:RegisterSpell(CSMASH_ID)
        overpower = Rotor:RegisterSpell(OVERPOWER_ID)
        exec = Rotor:RegisterSpell(EXEC_ID)
        vrush = Rotor:RegisterSpell(VRUSH_ID)
        bers_rage = Rotor:RegisterSpell(BERS_RAGE_ID)
        Rotor:RegisterSpell(RECKL_ID, 7)
        Rotor:RegisterOffGcdSpell(DCALM_ID, 6)
        Rotor:RegisterSpell(BLADESTORM_ID, 5)
        Rotor:RegisterOffGcdSpell(SSTRIKES_ID, 8, 'reminder')
    elseif IsProt then
        shield_slam = Rotor:RegisterSpell(SHIELD_SLAM_ID)
        revenge = Rotor:RegisterSpell(REVENGE_ID)
        devastate = Rotor:RegisterSpell(DEVASTATE_ID)
        shockwave = Rotor:RegisterSpell(SHOCKWAVE_ID, 8, 'reminder')
        concblow = Rotor:RegisterSpell(CONCBLOW_ID, 5, 'reminder')
        rend = Rotor:RegisterSpell(REND_ID)
        Rotor:RegisterSpell(LAST_STAND_ID, 6, 'reminder')
        Rotor:RegisterSpell(SHIELD_WALL_ID, 7, 'reminder')
        tclap = Rotor:RegisterSpell(TCLAP_ID, 2, 'reminder')
        dshout = Rotor:RegisterSpell(DSHOUT_ID)
    end
end

function Mod:Init(opts)
    IsArms = Rotor.spec == 1
    IsProt = Rotor.spec == 3
    self:RegisterAuras()
    self:RegisterSpells()
    Rotor:showPowerDisplay()
    return true
end

local function CheckUnsent(spell, time_since_last_sent)
    return (spell.sentAt <= spell.doneAt) and (spell.doneAt + (time_since_last_sent or 0.5) < now)
end

local function CheckOffCdAndUnsent(spell, wth)
    return (spell.cd - (wth or 0.4) <= gcd) and CheckUnsent(spell)
end

function Mod:NextSkill(gcd_arg, cur_spell)
    gcd = gcd_arg + 0.1 --max(0.5, gcd_arg)
    now = GetTime()

    if IsArms then
        return Mod:NextArmsSkill(gcd_arg, cur_spell)
    elseif IsProt then
        return Mod:NextProtSkill(gcd_arg, cur_spell)
    else
        return 0
    end
end

function Mod:NextArmsSkill(gcd_arg, cur_spell)
    if rend_debuff.time_left < 2 then
        return REND_ID
    end

    if first_csmash then
        if csmash_debuff.time_left > 0 then
            first_csmash = false
        else
            return CSMASH_ID
        end
    end

    if CheckOffCdAndUnsent(ms, 0) then
        return MS_ID
    end

    if CheckOffCdAndUnsent(bshout) then
        return BSHOUT_ID
    end

    if (csmash_debuff.time_left < gcd_arg) and CheckOffCdAndUnsent(csmash) then
        return CSMASH_ID
    end

--[[
    if (cur_spell ~= 0) and (gcd_arg <= 0.5) then
        return cur_spell
    end
]]
    if overpower.usable then
        return OVERPOWER_ID
    end

    if vrush.usable then
        return VRUSH_ID
    end

    if (UnitHealth('target') / Rotor.target_max_hp < 0.2) and ((exec_buff.time_left < 1) or (exec_buff.count < 5) or (Rotor.power > 30)) then
        return EXEC_ID
    else
        return SLAM_ID
    end
end

function Mod:NextProtSkill(gcd_arg, cur_spell)
    if not Rotor.in_combat then
        if CheckOffCdAndUnsent(charge) then
            return CHARGE_ID
        elseif CheckOffCdAndUnsent(hleap) and CheckUnsent(charge) then
            return HLEAP_ID
        else
            return REND_ID
        end
    end

    if dshout_debuff.time_left < 0.001 then
        return DSHOUT_ID
    end

    if (tclap_debuff.time_left < 0.001) and CheckOffCdAndUnsent(tclap) then
        return TCLAP_ID
    end

    if CheckOffCdAndUnsent(cshout) then
        return CSHOUT_ID
    end

    if CheckOffCdAndUnsent(shield_slam) then
        return SHIELD_SLAM_ID
    end

    if revenge.usable and CheckOffCdAndUnsent(revenge) then
        return REVENGE_ID
    end

    if CheckUnsent(devastate, 2) then
        return DEVASTATE_ID
    end

    -- Filler

    if (rend_debuff.time_left < 1) and CheckUnsent(rend, 2) then
        return REND_ID
    end

    if (dshout_debuff.time_left < 2) and CheckUnsent(dshout, 2) then
        return DSHOUT_ID
    end

    if (tclap_debuff.time_left < 2) and CheckOffCdAndUnsent(tclap) then
        return TCLAP_ID
    end

    if CheckOffCdAndUnsent(shockwave) then
        return SHOCKWAVE_ID
    end

    -- nothing to do here
    return DEVASTATE_ID
end

function Mod:TargetChanged()
    if csmash and (csmash.cd < 0.5) then
        first_csmash = true
    end
end

-- Command line handler
function Mod:HandleCommandLine(cmd)
    local cmd, args = strsplit(' ', (cmd or ''):lower(), 2)
    -- handle command line here
end

-- Module detection
function Mod:Detect()
    return (Rotor.spec == 1) or (Rotor.spec == 3)
end
